package cn.seaboot.admin.logger.manager;

import cn.seaboot.admin.logger.bean.AccessLog;
import cn.seaboot.admin.logger.service.AccessLogService;
import cn.seaboot.commons.core.CommonUtils;
import cn.seaboot.commons.core.FastJsonUtils;
import cn.seaboot.commons.file.ReaderUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * 日志处理器，对于Controller中使用了ActionLogger注解的接口，会将本次操作内容记录到数据库中
 *
 * @author 12614 on 2018/5/11.
 */
@ControllerAdvice
public class ActionLoggerAdvice implements ResponseBodyAdvice<Object> {
    private final Logger logger = LoggerFactory.getLogger(ActionLoggerAdvice.class);

    @Resource
    private AccessLogService logService;

    /**
     * {@inheritDoc}
     *
     * @param methodParameter
     * @param aClass
     * @return
     */
    @Override
    public boolean supports(@NotNull MethodParameter methodParameter, @NotNull Class<? extends HttpMessageConverter<?>> aClass) {
        return methodParameter.getMethodAnnotation(ActionLogger.class) != null;
    }

    /**
     * {@inheritDoc}
     *
     * @param o
     * @param methodParameter
     * @param mediaType
     * @param clazz
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @return
     */
    @Override
    public Object beforeBodyWrite(Object o, @NotNull MethodParameter methodParameter, @NotNull MediaType mediaType,
                                  @NotNull Class<? extends HttpMessageConverter<?>> clazz, @NotNull ServerHttpRequest serverHttpRequest,
                                  @NotNull ServerHttpResponse serverHttpResponse) {
        try {
            ActionLogger actionLogger = methodParameter.getMethodAnnotation(ActionLogger.class);
            if (actionLogger == null) {
                return o;
            }

            // unknown request type
            if (!(serverHttpRequest instanceof ServletServerHttpRequest)) {
                logger.debug("unknown request type: " + serverHttpRequest.getClass());
                return o;
            }

            HttpServletRequest request = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest();

            // 数据库日志
            AccessLog accessLog = new AccessLog();

            // request method / path
            accessLog.setRequestMethod(request.getMethod());
            accessLog.setServletPath(request.getServletPath());

            // request-parameter
            Map<String, Object> params = CommonUtils.getRequestParams(request);

            // remember include parameter
            String[] include = actionLogger.include();
            if (CommonUtils.isNotEmpty(include)) {
                Map<String, Object> using = new HashMap<>();
                for (String key : include) {
                    using.put(key, params.get(key));
                }
                params = using;
            }

            // remove exclusive parameter
            String[] exclusive = actionLogger.exclusive();
            if (CommonUtils.isNotEmpty(exclusive)) {
                for (String s : exclusive) {
                    params.remove(s);
                }
            }
            String paramsJson = FastJsonUtils.toJSONString(params);
            accessLog.setRequestParameter(paramsJson);

            // request-body already read, can not read again

            // response-body
            accessLog.setResponseBody(FastJsonUtils.toJSONString(o));

            // logger type, level
            accessLog.setLogType(actionLogger.type());
            accessLog.setLogLevel(actionLogger.level().value());
            accessLog.setOperation(actionLogger.operation());

            logService.saveLog(accessLog);
        } catch (Exception e) {
            // IGNORE 无法记录日志的场合不要影响业务进行
            logger.error("记录接口访问日志发生异常：", e);
        }
        return o;
    }
}
